const fetch = require('isomorphic-fetch');
const fs = require('fs');
const jsdom = require('jsdom');
const inquirer = require('inquirer')
const iconv = require('iconv-lite');
const { log, error, message, logCustom, purpleText, greenText, redText, yellowText } = require('../utils/log');
const PromiseOnly = require('../utils/PromiseOnly');
const Spin = require('../utils/spin');
const { JSDOM } = jsdom;
const publicConfig = {
    url: '',
    maximumConcurrent: 10,
    chapterReplaceList: [],
    chapterContentSelector: '#content'
};
function request(url) {
    return fetch(publicConfig.url + url,
                {
                    method: "GET",
                    headers: {}
                }
            ).then(res => {
                const contentType = res.headers.get('Content-Type');
                // 解析Content-Type头以获取字符集
                const charset = contentType.match(/charset=([\w-]+)/i)?.pop();
                // 输出字符集
                return res.arrayBuffer().then(sd => {
                    const buffer = Buffer.from(sd);
                    const utf8String = iconv.decode(buffer, charset || 'utf8');
                    return utf8String;
                })
            })
}
function getEveryPage({ url, title }) {
    const spin = new Spin(yellowText(title + ' downloading...\n'));
    spin.start();
    return request(url).then(res => {
        const data = res;
        const dom = new JSDOM(data);
        const contentDom = dom.window.document.querySelector(publicConfig.chapterContentSelector);
        let content = publicConfig.chapterReplaceList.reduce((t, v) => t.replace(v.test, v.target), contentDom.innerHTML);
        let sliceIndex;
        content = content
        .slice(0, (sliceIndex = content.indexOf('蓝星，夏国。')) !== -1 ? sliceIndex : undefined)
        .slice(0, (sliceIndex = content.indexOf('喜欢说好的直播算命')) !== -1 ? sliceIndex : undefined)
        .slice(0, (sliceIndex = content.indexOf('无尽的昏迷过后')) !== -1 ? sliceIndex : undefined)
        .slice(0, (sliceIndex = content.indexOf('网页版章节内容慢')) !== -1 ? sliceIndex : undefined)
        .slice(0, (sliceIndex = content.indexOf('【ps')) !== -1 ? sliceIndex : undefined).replace(/^\n{1,} {0,}/i,'');
        spin.succeed(greenText(title + ' download success.'));
        return title + '\n\n    ' + content + '\n\n';
    }).catch(err => {
        spin.fail(redText(title + ' download error.', redText(err)));
        return '';
    })
}
async function requestAll(arr) {
    let arrs = [];
    let obj = {};
    await Promise.all(arr.map(v => PromiseOnly.getPromiseOnly(publicConfig.maximumConcurrent)
    .add(() => getEveryPage(v).then(res => {
        if (!res) arrs.push({
            ...v
        })
        obj[v.index] = res;
    }))))
    // for (let i = 0; i < arr.length; i++) {
    //     await getEveryPage(arr[i]).then(res => {
    //         if (!res) arrs.push({
    //             ...arr[i]
    //         })
    //         obj[arr[i].index] = res;
    //     })
    // }
    if (arrs.length) {
        return Object.assign(obj, await requestAll(arrs));
    } else {
        return obj;
    }
}
//  * @param param1.maximumConcurrentChapter 同时获取的最大章节数 默认 10
/**
 * 获取指定页面的文本
 * @param param0.baseUrl 公共url  如：https://www.baidu.com
 * @param param0.chapterListUri 目录uri  如 /xxx/
 * @param param0.chapterListSelector 目录列表uri选择器  默认 'dl dd a'
 * @param param0.chapterStartIndex 目录列表第一章的下标  默认 '0'
 * @param param0.outputFileName 生成的文件名称  默认后缀名为.txt
 * @param param1.chapterContentSelector 正文元素选择器 默认 #content
 * @param param1.chapterReplaceList 正文中无用的元素替换集合 默认 替换<br><br/> &nbsp; \t 
 * @returns 
 */
let obtainBook = async ({
    baseUrl,
    chapterListUri,
    chapterListSelector = 'dl dd a',
    chapterStartIndex = 0,
    outputFileName,
}, {
    maximumConcurrentChapter = 10,
    chapterContentSelector = '#content',
    chapterReplaceList = [
        {
            test: /<p>[\s\S]*?<\/p>/i,
            target: ''
        },
        {
            test: /<!--[\s\S]*?-->/g,
            target: ''
        },
        {
            test: /&nbsp;/g,
            target: ' '
        },
        {
            test: /<br\/?>/g,
            target: '\n'
        },
        {
            test: new RegExp('try{ggauto();} catch(ex){}', 'g'),
            target: ''
        },
        {
            test: new RegExp('(adsbygoogle = window.adsbygoogle || []).push({})', 'g'),
            target: ''
        },
        {
            test: /\n{1,}/g,
            target: '\n\n    '
        },
        {
            test: /\t/g,
            target: ''
        },
        {
            test: /<p {0,}(.*?)>/g,
            target: ''
        },
        {
            test: /<\/p>/g,
            target: ''
        },
        {
            test: / {1,}/g,
            target: ''
        },
        {
            test: /\n{3,} {0,}/g,
            target: '\n\n       '
        },
        {
            test: /^\n{1,}/i,
            target: ''
        },
        {
            test: / {1,}$/i,
            target: ''
        },
        {
            test: /\n\n/g,
            target: "\n\n    "
        },
    ],
} = {}) => {
    if (!baseUrl || !chapterListUri || !outputFileName) {
        console.error('缺少必要参数 baseUrl or chapterListUri or outputFileName');
        return;
    }
    publicConfig.url = baseUrl.endsWith('/') ? baseUrl.slice(0, baseUrl.length - 1) : baseUrl;
    publicConfig.maximumConcurrent = maximumConcurrentChapter;
    publicConfig.chapterReplaceList = chapterReplaceList;
    publicConfig.chapterContentSelector = chapterContentSelector;
    const spin = new Spin(yellowText(outputFileName + ' 章节目录获取中...'));
    spin.start();
    const data = await request(chapterListUri).catch(() => {
        spin.fail(redText('目录获取失败，请重试!'));
        return null;
    });
    if (!data) {
        return;
    } else {
        spin.succeed(greenText('目录获取成功。'));
    }
    const dom = new JSDOM(data);
    const dla = dom.window.document.querySelectorAll(chapterListSelector);
    const arr = Array.prototype.slice.call(dla).slice(chapterStartIndex).map((v, i) => ({
        url: v.getAttribute('href').replace(baseUrl, ''),
        index: i,
        title: v.innerHTML.replace(/(^[\n ]{1,})|([\n ]{1,}$)/g, ''),
    }));
    if (!arr.length) {
        error('未读取到章节目录，请检查输入后重试！')
        return;
    } else {
        logCustom('#f97316')('  章节目录信息如下:');
        log('')
        log('       ' + purpleText('章节总数为: ') + greenText(arr.length + ''));
        log('')
        log('       ' + purpleText('第一章标题为: ') + greenText(arr[0].title));
        log('')
        log('       ' + purpleText('最后一章标题为为: ') + greenText(arr[arr.length - 1].title));
        log('')
    }
    let isContinue = await inquirer.prompt([
        {
            name: 'ok',
            type: 'confirm',
            message: "是否获取以上所有章节",
        }
    ]).then(answers => answers.ok)
    log(isContinue)
    if (!isContinue) {
        return;
    }
    const testArticle = await getEveryPage(arr[0]);
    message('第一章文章正文处理结果如下:')
    log('')
    log(testArticle)
    log('')
    isContinue = await inquirer.prompt([
        {
            name: 'ok',
            type: 'confirm',
            message: "是否按照以上格式获取其他文章",
        }
    ]).then(answers => answers.ok)
    if (!isContinue) {
        return;
    }
    const obj = await requestAll(arr);
    const latestSpin = new Spin(yellowText('所有文章获取完毕，文件生成中...'));
    latestSpin.start();
    const texts = arr.reduce((t, v) => t + obj[v.index], '')
    fs.writeFile(`../txt/${outputFileName}.txt`, texts, (err, data) => {
        if (err) {
            latestSpin.fail(redText('生成失败!'));
        } else {
            latestSpin.succeed(greenText('生成成功。'));
        }
    })

}

module.exports = obtainBook;

